local M = {
  -- Debugger tool
  "mfussenegger/nvim-dap",
  event = "VeryLazy",
  enabled = true,
  -- 系统安装 lldb 工具
  dependencies = {
    {
      "rcarriga/nvim-dap-ui",
      dependencies = {
        "mfussenegger/nvim-dap",
        "nvim-neotest/nvim-nio",
      },
    },

    -- 变量信息变化所见即所得 -- 不是必须的
    "theHamsta/nvim-dap-virtual-text",

    -- 好看ui提示 -- 不是必须的
    "nvim-telescope/telescope-dap.nvim",

    {
      -- https://miguelcrespo.co/posts/debugging-javascript-applications-with-neovim
      "mxsdev/nvim-dap-vscode-js",
      -- tag = "v1.1.0",
    },
    {
      "microsoft/vscode-js-debug",
      -- tag = "v1.74.1",
      -- build = "npm install --legacy-peer-deps && npx gulp vsDebugServerBundle && rm -rvf out && mv dist out",
      build = "pnpm install && npx gulp vsDebugServerBundle && rm -rvf out && mv dist out",
    },
  },
}

function M.config()
  local status, dap = pcall(require, "dap")
  if not status then
    vim.notify "Not found 'nvim-dap' plugin"
    return
  end

  local dadap_virtual_text_status, dadap_virtual_text = pcall(require, "nvim-dap-virtual-text")
  if not dadap_virtual_text_status then
    vim.notify "Not found 'nvim-dap-virtual-text' plugin"
    return
  end
  dadap_virtual_text.setup {}

  local telescope = require "telescope"
  vim.keymap.set("n", "<Leader>dc", function()
    telescope.extensions.dap.configurations {}
  end, { desc = "Debug Lunch Configurations" })

  vim.api.nvim_set_hl(0, "DapBreakpoint", { ctermbg = 0 })
  vim.api.nvim_set_hl(0, "DapLogPoint", { ctermbg = 0 })
  vim.api.nvim_set_hl(0, "DapStopped", { ctermbg = 0 })

  local dap_breakpoint = {
    error = {
      text = "🛑",
      texthl = "DapBreakpoint",
      linehl = "DapBreakpoint",
      numhl = "DapBreakpoint",
    },
    condition = {
      text = "󰟃",
      texthl = "DapBreakpoint",
      linehl = "DapBreakpoint",
      numhl = "DapBreakpoint",
    },
    rejected = {
      text = "󰃤",
      texthl = "DapBreakpint",
      linehl = "DapBreakpoint",
      numhl = "DapBreakpoint",
    },
    logpoint = {
      text = "",
      texthl = "DapLogPoint",
      linehl = "DapLogPoint",
      numhl = "DapLogPoint",
    },
    stopped = {
      text = "󰜴",
      texthl = "DapStopped",
      linehl = "DapStopped",
      numhl = "DapStopped",
    },
  }

  vim.fn.sign_define("DapBreakpoint", dap_breakpoint.error)
  vim.fn.sign_define("DapBreakpointCondition", dap_breakpoint.condition)
  vim.fn.sign_define("DapBreakpointRejected", dap_breakpoint.rejected)
  vim.fn.sign_define("DapLogPoint", dap_breakpoint.logpoint)
  vim.fn.sign_define("DapStopped", dap_breakpoint.stopped)

  -- ===================================== dapui ==========================
  local dapui = require "dapui"
  dapui.setup()

  -- https://github.com/rcarriga/nvim-dap-ui
  -- 显示断点变量作用域栈信息
  vim.keymap.set("n", "<leader>dd", function()
    dapui.toggle()
  end, { desc = "Show DAP Pages" })

  -- 运行端点后自动调用打开 dapui 界面
  dap.listeners.before.attach.dapui_config = function()
    dapui.open()
  end
  dap.listeners.before.launch.dapui_config = function()
    dapui.open()
  end
  dap.listeners.before.event_terminated.dapui_config = function()
    dapui.close()
    dap.repl.close()
  end
  dap.listeners.before.event_exited.dapui_config = function()
    dapui.close()
    dap.repl.close()
  end
  -- ===================================== dapui ==========================

  -- 详细请参考
  -- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation
  -- ============================ python 断点配置 =============================
  dap.adapters.python = function(cb, config)
    if config.request == "attach" then
      ---@diagnostic disable-next-line: undefined-field
      local port = (config.connect or config).port
      ---@diagnostic disable-next-line: undefined-field
      local host = (config.connect or config).host or "127.0.0.1"
      cb {
        type = "server",
        port = assert(port, "`connect.port` is required for a python `attach` configuration"),
        host = host,
        options = {
          source_filetype = "python",
        },
      }
    else
      cb {
        type = "executable",
        -- command = 'path/to/virtualenvs/debugpy/bin/python',
        command = "/usr/bin/python3",
        args = { "-m", "debugpy.adapter" }, -- 需要安装 pip install debugpy
        -- 这里的name对应下面configurations中的type
        name = "python",
        options = {
          source_filetype = "python",
        },
      }
    end
  end

  dap.configurations.python = {
    {
      -- The first three options are required by nvim-dap
      type = "python", -- the type here established the link to the adapter definition: `dap.adapters.python`
      request = "launch",
      name = "Debugpy Launch Debug",
      program = "${file}", -- This configuration will launch the current file if used.
      args = { "--debug" },
      pythonPath = function()
        local cwd = vim.fn.getcwd()
        if vim.fn.executable(cwd .. "/venv/bin/python") == 1 then
          return cwd .. "/venv/bin/python"
        elseif vim.fn.executable(cwd .. "/.venv/bin/python") == 1 then
          return cwd .. "/.venv/bin/python"
        else
          return "/usr/bin/python"
        end
      end,
    },
  }
  -- ============================ python 断点配置 =============================

  -- ================ C/C++/Rust (via lldb-vscode) 断点配置 =================
  dap.adapters.lldb = {
    type = "executable",
    -- 这里指向lldb-vscode的实际路径  which lldb-vscode
    -- 参考 https://zhuanlan.zhihu.com/p/590908735
    -- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation
    -- lldb-vscode lldb-server 提前编译好的路径
    -- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#ccrust-via-lldb-vscode
    -- lldb-vscode rename lldb-dap
    command = "/usr/bin/lldb-dap", -- adjust as needed, must be absolute path
    name = "lldb", -- 这里的name对应下面configurations中的type
  }

  -- 配置 c++
  dap.configurations.cpp = {
    {
      name = "Cpp Launch Debug",
      type = "lldb",
      request = "launch",
      program = function()
        -- 需要执行断点的程序路径
        return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
      end,
      cwd = "${workspaceFolder}",
      stopOnEntry = false,
      args = {},
      env = function()
        local variables = {}
        for k, v in pairs(vim.fn.environ()) do
          table.insert(variables, string.format("%s=%s", k, v))
        end
        return variables
      end,
    },
  }
  -- 配置 c
  dap.configurations.c = dap.configurations.cpp

  dap.adapters.gdb = {
    type = "executable",
    command = "gdb",
    args = { "--interpreter=dap", "--eval-command", "set print pretty on" },
  }
  dap.configurations.c = {
    {
      name = "Launch",
      type = "gdb",
      request = "launch",
      program = function()
        return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
      end,
      cwd = "${workspaceFolder}",
      stopAtBeginningOfMainSubprogram = false,
    },
    {
      name = "Select and attach to process",
      type = "gdb",
      request = "attach",
      program = function()
        return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
      end,
      pid = function()
        local name = vim.fn.input "Executable name (filter): "
        return require("dap.utils").pick_process { filter = name }
      end,
      cwd = "${workspaceFolder}",
    },
    {
      name = "Attach to gdbserver :1234",
      type = "gdb",
      request = "attach",
      target = "localhost:1234",
      program = function()
        return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
      end,
      cwd = "${workspaceFolder}",
    },
  }

  -- 配置 rust
  dap.configurations.rust = dap.configurations.cpp
  dap.configurations.rust = {
    {
      name = "Rust Launch Debug",
      type = "lldb",
      request = "launch",
      -- program = function()
      --   -- 执行命令 cargo build
      --   vim.fn.jobstart("cargo build")
      --   return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
      -- end,
      program = function()
        -- 获取程序打包后的文件, 注意先 cargo run 运行打包一下,否这找不到
        -- vim.notify(target_dir .. "/debug/" .. target_name, vim.log.levels.INFO, {})
        vim.fn.jobstart "cargo build"
        local metadata_json = vim.fn.system "cargo metadata --format-version 1 --no-deps"
        local metadata = vim.fn.json_decode(metadata_json)
        local target_name = metadata.packages[1].targets[1].name
        local target_dir = metadata.target_directory
        return target_dir .. "/debug/" .. target_name
      end,

      cwd = "${workspaceFolder}",
      stopOnEntry = false,
      -- args = {},
      -- 如果需要一些参数
      args = function()
        -- 同样的进行命令行参数指定
        local inputstr = vim.fn.input("CommandLine Args:", "")
        local params = {}
        for param in string.gmatch(inputstr, "[^%s]+") do
          table.insert(params, param)
        end
        return params
      end,
      showDisassembly = "never",
      initCommands = function()
        -- Find out where to look for the pretty printer Python module
        local rustc_sysroot = vim.fn.trim(vim.fn.system "rustc --print sysroot")

        local script_import = 'command script import "' .. rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py"'
        local commands_file = rustc_sysroot .. "/lib/rustlib/etc/lldb_commands"

        local commands = {}
        local file = io.open(commands_file, "r")
        if file then
          for line in file:lines() do
            table.insert(commands, line)
          end
          file:close()
        end
        table.insert(commands, 1, script_import)

        return commands
      end,
    },
  }
  -- ================ C/C++/Rust (via lldb-vscode) 断点配置 =================

  -- ================ javascript 断点配置 =================
  --#region nodejs 断点配置
  dap.set_log_level "TRACE"
  -- https://miguelcrespo.co/posts/debugging-javascript-applications-with-neovim
  require("dap-vscode-js").setup {
    -- node_path = "node", -- Path of node executable. Defaults to $NODE_PATH, and then "node"
    -- vim.fn.stdpath('data') == $HOME/.local/share/nvim
    debugger_path = vim.fn.stdpath "data" .. "/lazy/vscode-js-debug",
    adapters = {
      "chrome",
      "pwa-node",
      "pwa-chrome",
      "pwa-msedge",
      "node-terminal",
      "pwa-extensionHost",
      "node",
      "chrome",
    },
  }

  local js_based_languages = { "typescript", "javascript", "typescriptreact" }

  for _, language in ipairs(js_based_languages) do
    dap.configurations[language] = {
      {
        type = "pwa-node",
        request = "launch",
        name = "Launch file",
        program = "${file}",
        cwd = "${workspaceFolder}",
        runtimeExecutable = "node",
      },
      {
        type = "pwa-node",
        request = "attach",
        name = "Attach",
        processId = require("dap.utils").pick_process,
        cwd = "${workspaceFolder}",
        runtimeExecutable = "node",
      },
      {
        type = "pwa-chrome",
        request = "launch",
        name = 'Start Chrome with "localhost"',
        url = "http://localhost:8989",
        webRoot = "${workspaceFolder}",
        userDataDir = "${workspaceFolder}/.vscode/vscode-chrome-debug-userdatadir",
      },
    }
  end
  --#endregion
  -- ================ javascript 断点配置 =================

  -- ================================ 断点快捷键配置 =========================
  local keymap = vim.keymap

  -- 查看默认的配置 :help dap-mapping
  --[[
  -- Set keymaps to control the debugger
  vim.keymap.set('n', '<F5>', require 'dap'.continue)
  vim.keymap.set('n', '<F10>', require 'dap'.step_over)
  vim.keymap.set('n', '<F11>', require 'dap'.step_into)
  vim.keymap.set('n', '<F12>', require 'dap'.step_out)
  vim.keymap.set('n', '<leader>b', require 'dap'.toggle_breakpoint)
  vim.keymap.set('n', '<leader>B', function()
    require 'dap'.set_breakpoint(vim.fn.input('Breakpoint condition: '))
  end)
  -- telescope 替换 require('dap').continue() 好看一些
  require('telescope').extensions.dap.configurations {}
  require('telescope').extensions.dap.commands {}
  require('telescope').extensions.dap.list_breakpoints {}
  require('telescope').extensions.dap.variables {}
  require('telescope').extensions.dap.frames {}
  ]]

  -- https://github.com/mfussenegger/nvim-dap/tree/56118cee6af15cb9ddba9d080880949d8eeb0c9f
  -- 断点设置/取消
  keymap.set("n", "<Leader>db", dap.toggle_breakpoint, { desc = "Debug: Toggle Breakpoint" })
  -- 设置断点
  keymap.set("n", "<Leader>DB", dap.set_breakpoint, { desc = "Debug: Set Breakpoint" })
  keymap.set("n", "<leader>dB", function()
    dap.set_breakpoint(vim.fn.input "Breakpoint condition: ")
  end, { desc = "Debug: Set Breakpoint" })
  keymap.set("n", "<Leader>lp", function()
    dap.set_breakpoint(nil, nil, vim.fn.input "Log point message: ")
  end, { desc = "Show Message" })
  -- 开始运行
  keymap.set("n", "<Leader>dr", dap.run_last, { desc = "Debug: Run Last" })
  -- 下一步
  keymap.set("n", "<F5>", dap.continue, { desc = "Debug: Start/Continue" })
  -- 进入
  keymap.set("n", "<F1>", dap.step_into, { desc = "Debug: Step Into" })
  -- 单步
  keymap.set("n", "<F2>", dap.step_over, { desc = "Debug: Step Over" })
  -- 跳出
  keymap.set("n", "<F3>", dap.step_out, { desc = "Debug: Step Out" })
  keymap.set("n", "<Leader>dl", dap.repl.open, { desc = "Open repl" })

  keymap.set({ "n", "v" }, "<Leader>dh", function()
    require("dap.ui.widgets").hover()
  end, { desc = "Widgets Hover" })
  keymap.set({ "n", "v" }, "<Leader>dp", function()
    require("dap.ui.widgets").preview()
  end, { desc = "Preview" })
  keymap.set("n", "<Leader>df", function()
    local widgets = require "dap.ui.widgets"
    widgets.centered_float(widgets.frames)
  end, { desc = "Widgets Frames" })
  keymap.set("n", "<Leader>du", function()
    local widgets = require "dap.ui.widgets"
    widgets.centered_float(widgets.scopes)
  end, { desc = "Widgets Scopes" })
  -- ================================ 断点快捷键配置 =========================
end

return M
